import { App, DirectiveBinding, getCurrentInstance } from 'vue';
import Views from '../types/views';
import Creater from '../types/creater';
import { convertUnits, getPopupStr } from './tools';
import Pools from '../types/pools';
import { version } from '../../package.json';
import { EpgFocusEvent, FocusEventName, OptionType } from '../types/focus';
import MoveRule from './moveRule';
import { useRoute } from 'vue-router';

export default class Service extends MoveRule {
  private _monitor: boolean = true;
  private _vm: App;
  private activeListenFlag: boolean = false;
  private _durationState: boolean = false;
  private _durationTimeout: number = 0;
  private _baseEvents: EpgFocusEvent = { default: { rootPopup: {} } }
  private popEvents: { [key: string]: { [key in FocusEventName]?: (event: Event) => void } } = { rootPopup: {} };
  private _version: string = version;
  protected defOption: OptionType = {
    inactiveTime: 0,
    limitTime: 200
  };
  constructor(app: App, options?: OptionType) {
    super(options)
    if (options) {
      let { inactiveTime, limitTime, back } = options;
      inactiveTime !== undefined && (this.defOption.inactiveTime = Math.max(convertUnits(inactiveTime), 300000));
      limitTime !== undefined && (this.defOption.limitTime = Math.max(convertUnits(limitTime), 200));
      back !== undefined && (this.defOption.back = back)
    }
    this._vm = app
    this.resetEvent()
    this.startActiveListen()
    this.startEventListen()
  }

  get monitor() {
    return this._monitor;
  }

  set monitor(flag: boolean) {
    this._monitor = flag
    flag && this.getEventName("ENTER")
  }
  /**
   * 查看版本号
   */
  get version() {
    return this._version;
  }

  /**
   * 重置按键响应事件
   */
  get resetEvent() {
    return () => {
      for (const popEvents of Object.values(this._baseEvents)) {
        for (const prop of Object.keys(popEvents.rootPopup)) {
          const eName = prop as FocusEventName
          popEvents.rootPopup[eName] = undefined
        }
      }
    }
  }

  /**
   * 添加事件
   * @param eventObj 事件对象
   * @param popup 弹层
   */
  get addEvent() {
    return (eventObj: { [k in FocusEventName]?: () => void }, popup?: string) => {
      const viewName = this.getRouteName()
      const popupStr = getPopupStr(popup)
      !Object.keys(this._baseEvents).includes(viewName) && (this._baseEvents[viewName] = {})
      Object.keys(this._baseEvents[viewName]).includes(popupStr) ?
        Object.assign(this._baseEvents[viewName][popupStr], eventObj) :
        (this._baseEvents[viewName][popupStr] = eventObj)
    }
  }

  private getRouteName(): string {
    let viewName: string = '';
    const instance = getCurrentInstance()
    if (this._vm.config.globalProperties.$router && instance) {
      const { type } = instance;
      const route = useRoute();
      // 获取当前视图名称
      for (const record of route.matched) {
        for (const [name, component] of Object.entries(record.components!)) {
          if (component === type) {
            viewName = name;
            break;
          }
        }
      }
    }
    return viewName || "default";
  }

  private startEventListen() {
    let limitTime: number = convertUnits(this.defOption.limitTime!)
    let startTime: number = new Date().getTime()
    let storeKey: number | null
    const eventDown = (event: KeyboardEvent) => {
      event = event || window.event
      // 组织默认事件兼容处理
      if (typeof event.cancelable !== 'boolean' || event.cancelable) {
        event.preventDefault()
      } else {
        console.warn("The following event couldn't be canceled:")
      }
      // // 阻止事件冒泡
      // if (event.stopPropagation) {
      //   event.stopPropagation()
      // } else if (typeof window !== 'undefined') {
      //   event.cancelBubble = true
      // }
      let keyCode = event.which ? event.which : event.keyCode
      if (this.useKeyUp) {
        if (!storeKey) {
          storeKey = keyCode
          startTime = Date.now()
        }
      } else {
        let currentTime = Date.now()
        let keyAction = this.getKeyAction(keyCode)
        let difference = currentTime - startTime
        // 使用节流的形式限制按键响应
        if (keyAction && this.monitor && difference > limitTime) {
          startTime = currentTime
          let eventName: FocusEventName | "" = this.getEventName(keyAction)
          eventName !== "" && this.callEvent(eventName, event)
        }
        if (this.activeListenFlag) {
          // 配置时长超过5分钟时生效
          this.exitInactive()
          this.timeoutInactive()
        }
      }
    }
    const eventUp = (event: KeyboardEvent) => {
      event = event || window.event
      // 组织默认事件兼容处理
      if (typeof event.cancelable !== 'boolean' || event.cancelable) {
        event.preventDefault()
      } else {
        console.warn("The following event couldn't be canceled:")
      }
      // // 阻止事件冒泡
      // if (event.stopPropagation) {
      //   event.stopPropagation()
      // } else if (typeof window !== 'undefined') {
      //   event.cancelBubble = true
      // }
      let currentTime = Date.now()
      let keyCode = event.which ? event.which : event.keyCode
      let keyAction = this.getKeyAction(keyCode)
      if (keyAction && this.monitor && keyCode === storeKey) {
        let longFlag = false
        if (currentTime - startTime >= 650) {
          longFlag = true
        }
        let eventName: FocusEventName | "" = this.getEventName(keyAction, longFlag)
        eventName !== "" && this.callEvent(eventName, event)
      }
      storeKey = null
      if (this.activeListenFlag) {
        // 配置时长超过5分钟时生效
        this.exitInactive()
        this.timeoutInactive()
      }
    }
    const eventOver = (event: MouseEvent) => {
      event = event || window.event
      let parent: HTMLElement = event.target as HTMLElement
      while (parent && parent._$FocusCreater === undefined) {
        parent = parent.parentElement as HTMLElement
      }
      if (parent._$FocusCreater) {
        const creater: Creater = parent._$FocusCreater;
        creater.name === "items" && getPopupStr(creater.popup) === getPopupStr(this.popSign) && creater !== this.pointer && this.initFocus(creater)
        if (this.activeListenFlag) {
          // 配置时长超过5分钟时生效
          this.exitInactive()
          this.timeoutInactive()
        }
      }
    }
    const eventClick = (event: MouseEvent) => {
      event = event || window.event
      let parent: HTMLElement = event.target as HTMLElement
      while (parent._$FocusCreater === undefined) {
        parent = parent.parentElement as HTMLElement
      }
      if (parent._$FocusCreater) {
        const creater: Creater = parent._$FocusCreater;
        if (creater.name === "items" && getPopupStr(creater.popup) === getPopupStr(this.popSign)) {
          this.pointer !== creater && this.initFocus(creater)
          this.callEvent("keyEnter", event)
        }
        if (this.activeListenFlag) {
          // 配置时长超过5分钟时生效
          this.exitInactive()
          this.timeoutInactive()
        }
      }
    }
    // 事件监听
    if (document.addEventListener) {
      document.addEventListener("mouseover", eventOver, true)
      document.addEventListener("click", eventClick, true)
      document.addEventListener('keydown', eventDown, true)
      // } else if ((document as any).attachEvent) {
      //   (document as any).attachEvent("onkeydown", eventDown);
      // } else {
      //   (document as any).onkeydown = eventDown
    }
    if (this.useKeyUp) {
      if (document.addEventListener) {
        document.addEventListener('keyup', eventUp, true)
        // } else if ((document as any).attachEvent) {
        //   (document as any).attachEvent("onkeyup", eventDown);
        // } else {
        //   (document as any).onkeyup = eventDown
      }
    }
  }

  private callEvent(eventName: FocusEventName, event: Event) {
    let viewName = this.pointer ? this.pointer.routeViewName : "default"
    !Object.keys(this._baseEvents).includes(viewName) && (viewName = "default")
    const popSignStr = getPopupStr(this.popSign)
    if (Object.keys(this._baseEvents[viewName]).includes(popSignStr)) {
      if (Object.keys(this._baseEvents[viewName][popSignStr]).includes(eventName)) {
        this._baseEvents[viewName][popSignStr]![eventName]!(event)
      }
      if (["keyLeft", "keyRight", "keyUp", "keyDown", "keyBack"].includes(eventName)) {
        switch (eventName) {
          case "keyLeft":
            this.moveFocus("left");
            break;
          case "keyRight":
            this.moveFocus("right");
            break;
          case "keyUp":
            this.moveFocus("up");
            break;
          case "keyDown":
            this.moveFocus("down");
            break;
          case "keyBack":
            popSignStr === "rootPopup" && (this.defOption.back !== undefined && typeof this.defOption.back === "function" ? this.defOption.back(event) : history.back());
            break;
        }
      }
    }
  }

  /**
   * 判断并启动非活跃状态监听
   */
  private startActiveListen() {
    this.activeListenFlag = false;
    if (this.defOption.inactiveTime !== undefined && this.defOption.InterruptInactive !== undefined && this.defOption.inactiveCallback !== undefined) {
      this.activeListenFlag = true;
      // 配置时长超过5分钟时生效
      this.exitInactive()
      this.timeoutInactive()
    }
  }

  /**
   * 退出非活跃状态
   */
  private exitInactive() {
    if (this._durationState) {
      this._durationState = false
      this.defOption.InterruptInactive?.call(this)
    }
  }

  /**
   * 非活跃状态计时
   */
  private timeoutInactive() {
    if (this._durationState) {
      this._durationTimeout = 0
    } else {
      clearTimeout(this._durationTimeout)
      this._durationTimeout = setTimeout(() => {
        // 满足不活跃时长后执行对应方法
        this._durationState = true
        this.defOption.inactiveCallback?.call(this)
      }, convertUnits(this.defOption.inactiveTime!))
    }
  }

  /**
   * 检测当前视图是否有未卸载的焦点/视图对象
   * @param views 当前节点所在视图对象
   * @returns 所有子视图、焦点是否已卸载
   */
  private hasChildren(views: Views) {
    let flag = false;
    for (let [propName, value] of Object.entries(views)) {
      if (propName === 'layers') {
        for (const layerPool of Object.values(value as { [key: string]: Pools })) {
          // 检测当前view的焦点列表是否卸载完
          flag = flag || !!layerPool.items.length;
          if (flag) {
            break;
          }
        }
      } else if (propName === 'routeViews') {
        for (const routeProp of Object.values(value as { [key: string]: { [key: string]: Views } })) {
          // 检测当前view是否含有子view
          flag = flag || !!Object.keys(routeProp).length;
          if (flag) {
            break;
          }
        }
      }
      if (flag) {
        break;
      }
    }
    return !flag;
  }

  /**
   * 注册节点
   * @param {Creater} creater 节点对象
   */
  get register() {
    return (creater: Creater) => {
      let { appoints, name, routeDepath, routeVpath, routerEl, sign } = creater;
      let views = this.getView(routeDepath, routeVpath);
      // 添加views指向
      creater.__views__ = views;
      views.registeLayer(creater);
      if (!views.__baseView__) {
        views.registeEl(routerEl, routeVpath, views, this);
      }
      if (name === 'items') {
        // 添加默认选中状态类名
        sign && creater.selected && creater.add(this.defOption.selectName!);
        // 添加默认焦点
        if (appoints && !creater.__pools__?.pointer) {
          creater.__pools__?.setPointer(creater);
          creater.add(this.defOption.focusName!);
        }
      }
    }
  }

  /**
   * 更新焦点/区域
   * @param elem 元素
   * @param binding 绑定信息
   */
  get updater() {
    return (elem: HTMLElement, binding: DirectiveBinding) => {
      let creater = elem._$FocusCreater;
      creater?.update(binding);
    }
  }

  /**
   * 卸载焦点及对应view
   * @param elem 元素
   */
  get remover() {
    return (elem: HTMLElement) => {
      let creater: Creater | undefined = elem._$FocusCreater;
      if (creater !== undefined) {
        creater.__pools__?.splicePool(creater);
        // 清空所有引用
        if (!creater.__pools__?.items.length) {
          // 解绑当前pools
          if (creater.popup) {
            // 节点在弹窗上，直接移除当前view上对应的弹窗属性
            const propStr = `popup_${creater.popup}`;
            delete creater.__views__?.layers[propStr];
            this.popEvents[creater.popup] && delete this.popEvents[creater.popup];
          }
          let views: Views | undefined = creater.__views__;
          while (views && this.hasChildren(views)) {
            if (views.parent) {
              for (const [propName, value] of Object.entries(views.parent.routeViews)) {
                for (const [viewProp, viewVal] of Object.entries(value as { [key: string]: Views })) {
                  if (viewVal === views) {
                    delete value[viewProp];
                  }
                }
                if (!Object.keys(value).length) {
                  delete views.parent.routeViews[propName];
                  if (views.parent.viewSign === propName) {
                    views.parent.viewSign = '';
                  }
                }
              }
            }
            views = views.parent;
          }
        }
        creater.__views__ = undefined;
        creater.__pools__ = undefined;
        creater.__group__ = undefined;
        elem._$FocusCreater = undefined;
      }
    }
  }
}
